﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web.UI;
using Ext.Net;
using KamLib.Controls;
using Newtonsoft.Json;
using Column = Ext.Net.Column;
using NumberColumn = Ext.Net.NumberColumn;


namespace KamLib
{
    public static class ExtUtils
    {


        public static T Try<T>(this StateBag state, string name)
        {
            var obj = state[name];
            return obj is T ? (T)obj : default(T);
        }

        public static T Try<T>(this StateBag state, string name, T defaultValue)
        {
            var obj = state[name];
            return obj is T ? (T)obj : defaultValue;
        }

        public static T Try<T>(this ViewStateProxy state, string name)
        {
            var obj = state[name];
            return obj is T ? (T)obj : default(T);
        }

        public static T Try<T>(this ViewStateProxy state, string name, T defaultValue)
        {
            var obj = state[name];
            return obj is T ? (T)obj : defaultValue;
        }


        #region Store

        public static void AddFields(this Store store, Type type)
        {
            var rdr = store.Reader.FirstOrDefault();
            if (rdr == null) return;

            rdr.Fields.Clear();
            foreach (var p in type.GetPropertiesAndFields().Where(p => p.GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Length == 0))
            {
                store.AddField(new RecordField(p.Name, TypeToFieldType(p)));
            }
        }

        public static void AddFields(this DataReader rdr, Type type)
        {
            rdr.Fields.Clear();
            rdr.Fields.AddRange(
                type.GetPropertiesAndFields()
                .Where(p => p.GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Length == 0)
                .Select(p => new RecordField(p.Name, TypeToFieldType(p), "M$")
                {
                    UseNull = p.ResultType().Name.StartsWith("Nullable")
                })
            );
        }

        public static void AddFields(this DataReader rdr, DbDataReader r)
        {
            rdr.Fields.Clear();
            for (var i = 0; i < r.FieldCount; ++i)
            {
                rdr.Fields.Add(
                    new RecordField(r.GetName(i), TypeToFieldType(r.GetFieldType(i))) { DateFormat = "M$" }
                );
            }
        }


        public static RecordFieldType TypeToFieldType(MemberInfo member)
        {
            if (member is PropertyInfo)
                return TypeToFieldType(((PropertyInfo)member).PropertyType);

            if (member is FieldInfo)
                return TypeToFieldType(((FieldInfo)member).FieldType);

            return RecordFieldType.Auto;
        }


        public static RecordFieldType TypeToFieldType(Type ptype)
        {
            if (ptype == typeof(DateTime) || ptype == typeof(DateTime?))
            {
                return RecordFieldType.Date;
            }
            if (ptype == typeof(int) || ptype == typeof(int?))
            {
                return RecordFieldType.Int;
            }
            if (ptype == typeof(double) || ptype == typeof(double?) ||
                ptype == typeof(decimal) || ptype == typeof(decimal?) ||
                ptype == typeof(float) || ptype == typeof(float?))
            {
                return RecordFieldType.Float;
            }
            if (ptype == typeof(bool) || ptype == typeof(bool?))
            {
                return RecordFieldType.Boolean;
            }
            return RecordFieldType.Auto;
        }

        public static Column NewColumn(Type ptype)
        {
            if (ptype == typeof(DateTime) || ptype == typeof(DateTime?))
            {
                return new DateTimeColumn();
            }
            if (ptype == typeof(int) || ptype == typeof(int?))
            {
                return new IntColumn();
            }
            if (ptype == typeof(double) || ptype == typeof(double?) ||
                ptype == typeof(decimal) || ptype == typeof(decimal?) ||
                ptype == typeof(float) || ptype == typeof(float?))
            {
                return new FloatColumn();
            }
            if (ptype == typeof(bool) || ptype == typeof(bool?))
            {
                return new YesNoColumn();
            }
            return new Column();
        }


        public static object ValueAsObject(this FilterCondition condition)
        {
            switch (condition.FilterType)
            {
                case FilterType.Boolean:
                    return condition.ValueAsBoolean;
                case FilterType.Date:
                    return condition.ValueAsDate;
                case FilterType.List:
                    return condition.ValuesList;
                case FilterType.Numeric:
                    return condition.ValueAsDouble;
                case FilterType.String:
                    return condition.Value;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        public static string SqlCondition(this GridFilters gridFilters, StoreRefreshDataEventArgs e)
        {
            var sfilter = e.Parameters[gridFilters.ParamPrefix];
            return sfilter.isnull() ? null : SqlCondition(new FilterConditions(sfilter));
        }

        public static string SqlCondition(FilterConditions fc)
        {
            var sb = new StringBuilder();

            foreach (var condition in fc.Conditions)
            {
                var value = condition.ValueAsObject();

                if (sb.Length > 0) sb.Append(" and ");
                sb.Append(condition.Name);
                switch (condition.Comparison)
                {
                    case Comparison.Eq:
                        switch (condition.FilterType)
                        {
                            case FilterType.List:
                                sb.Append(" in (");
                                foreach (var lvalue in (ReadOnlyCollection<string>)value)
                                {
                                    sb.Append(SystemDataHelper.ToParam(lvalue));
                                    sb.Append(", ");
                                }
                                sb.AppendLine(")");
                                break;
                            case FilterType.String:
                                sb.Append(" like ");
                                sb.Append(SystemDataHelper.ToParam(value + "%"));
                                break;
                            default:
                                sb.Append(" = ");
                                sb.Append(SystemDataHelper.ToParam(value));
                                break;
                        }
                        break;
                    case Comparison.Gt:
                        sb.Append(" >= ");
                        sb.Append(SystemDataHelper.ToParam(value));
                        break;
                    case Comparison.Lt:
                        sb.Append(" =< ");
                        sb.Append(SystemDataHelper.ToParam(value));
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
            return sb.ToString();
        }

        #endregion


        #region Parameters

        public static ParameterCollection AddValue(this ParameterCollection a, string name, string value)
        {
            a.Add(new Parameter { Name = name, Value = value, Mode = ParameterMode.Value });
            return a;
        }
        public static ParameterCollection AddRaw(this ParameterCollection a, string name, string value)
        {
            a.Add(new Parameter { Name = name, Value = value, Mode = ParameterMode.Raw });
            return a;
        }
        public static ParameterCollection AddRaw(this ParameterCollection a, string name, object value)
        {
            a.Add(new Parameter { Name = name, Value = JSON.Serialize(value), Mode = ParameterMode.Raw });
            return a;
        }

        #endregion


    }
}

